package okw.network;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Random;

import okw.database.DatabaseManager;
import okw.database.Election;
import okw.database.OKWConfiguration;
import okw.database.TerminalConfiguration;
import okw.gui.MainWindowO;

import core.messages.Info;
import core.messages.IMessage;

/**
 * 
 * @author Maks
 * Odpowiada za tworzenie po��czenia, umo�liwia wysy��nie wiadomo��i w g�r� (w d� trzeba b�dzie zrobi�)
 * 
 */

public class ConnectionO {

	ServerSocket SSocket = null;
	ArrayList<Terminal> terminale = new ArrayList<Terminal>();
	Queue queue = null;
	Queue2 queue2 = null;
	Thread sender = null;
	StanO stanO = null;
	Random generator = new Random();
	DatabaseManager db = null;
	Terminal serwerTerm = new Terminal(null, null, null, null, "0", "0", "serwer");
	
	public ArrayList<Terminal> getTerminale() {
		return terminale;
	}

	public void setTerminale(ArrayList<Terminal> terminale) {
		this.terminale = terminale;
	}
	
	public ConnectionO(){
		
	}
	
	// wysyla prosbe o przyslanie danych
	public void getData(String token) throws Exception{
		Info msg = new Info(stanO.getNr(), 4);
		msg.setToken(token);
		sendUp(msg);
	}
	
	//laczy sie jako klient do serwera wyzej
	public void connect(int port, String host) throws Exception{

		// pobiera dane z bazy
		OKWConfiguration okwConf = db.getOKWConfiguration();
		
		if(okwConf!=null){
			host = okwConf.getIp();
			System.out.println("ADRES " + okwConf.getIp());
		}
		
		Socket KlientS = new Socket(host, port);
		System.out.println("Lacze!");
		ObjectOutputStream oos = new ObjectOutputStream(KlientS.getOutputStream());
	
		// trzeba bedzie sprawdzic czy sie faktycznie polaczyl
		Thread receiver = new Thread(new ServerReceiver(serwerTerm ,this, queue2, stanO));
		serwerTerm.setSocket(KlientS);
		serwerTerm.setOos(oos);
		serwerTerm.setThread(receiver);
		receiver.start();
	}
	
	// startuje serwer OKW
	public void start(StanO stanO, DatabaseManager db){
		this.stanO = stanO;
		queue = new Queue();
		queue2 = new Queue2();
		sender = new Thread(new Sender(queue, queue2));
		sender.start();
		this.db = db;
		
		aktualizacjaTerminali(); // TODO: do usunięcia
	}
	
	// do odsiezania listy, po zmianie w bazie
	public void aktualizacjaTerminali() {
		ArrayList<okw.database.Terminal> terminals = (ArrayList<okw.database.Terminal>) db.getAll(okw.database.Terminal.class);
		
		for(Terminal t: terminale){
			try {
			if(t.getSocket()!=null)
				
					t.getSocket().close();
			} catch (IOException e) {
			e.printStackTrace();
			}
		}
		terminale.clear();
	
		for (okw.database.Terminal t : terminals){
			Terminal ter;
			if (t.getTerminalConfiguration() != null) {
				TerminalConfiguration config = db.get(TerminalConfiguration.class, t.getTerminalConfiguration().getId());
				System.out.println("* "+config.getIp());
				ter = new Terminal(null, null, null, null, t.getIdentificationField(), config.getIp(), t.getName());
				
			} else 
				ter = new Terminal(null, null, null, null, t.getIdentificationField(), "0", t.getName());
			terminale.add(ter);
		}
	}
	
	// startuje funkcjonalnosc serwera
	public void startServer(int port, InetAddress adres) throws IOException{
		if(SSocket == null){		
			SSocket = new ServerSocket(port, 999, adres);
			System.out.println("Serwer wystartowal!");
			stanO.setWlaczony(true);
		}
	}
	
	//nasluchuje jako serwer po��cze� od terminali do g�osowania
	public void listen() throws IOException{
		 Thread nasluchuj = new Thread(new Listen(this, SSocket, queue2, stanO));
		 nasluchuj.start();
	}
	
	// wysyla do serwera
	public void sendUp(IMessage msg) throws Exception{
		System.out.println("Komisja wysyla do centrali nr:msg: " + msg.getNumber() + " : " + msg.getType());
		if(serwerTerm.getOos()==null)
			throw new Exception("Nie udalo sie wyslac wiadomosci do serwera");
		if(msg.getClass().equals(Info.class)){
			core.messages.StanO StanTest = (core.messages.StanO) ((Info)msg).getStan();
			int abba = StanTest.getNr();
			System.out.println(StanTest + "  "  + abba);
		}
		QueueItem tmp = new QueueItem(msg, serwerTerm);
		queue.put(tmp);
	}
	
	// wysyla wiadomosc do terminala
	public void sendDown(Terminal t, IMessage msg) throws Exception{
		System.out.println("Komisja wysyla do terminala nr:msg: " + msg.getNumber() + " : " + msg.getType());
		if(t.getOos()==null)
			throw new Exception("Nie udalo sie wyslac wiadomosci do terminala " + t.getName() );
		QueueItem tmp = new QueueItem(msg, t);
		queue.put(tmp);
	}
	
	// rozpoczyna glosowanie
	public void startGlosowania(){
		for(int i = 0; i<terminale.size(); i++){
			Info info = new Info(stanO.getNr(),1);
			try {
				sendDown(terminale.get(i), info);
			} catch (Exception e) {
				e.printStackTrace();
				e.getMessage();
			}
		}		
		stanO.setGlosowanieRozpoczete(true);
		stanO.setKonczenieGlosowania(false);
		//stanO.setZaczacGlosowanie(false);
		MainWindowO.rozpoczetoGlosowanie();
	}
	
	// wysyla do wszystkich terminali NIE UZYWAC POZA TESTAMI!
	public void sendDownToAll(IMessage o){
		for(int i = 0; i<terminale.size(); i++){
			try {
				sendDown(terminale.get(i), o);
				//o.setNumber(stanO.getNr());
			} catch (Exception e) {
				e.printStackTrace();
				e.getMessage();
			}
		}
	}
	
	// konczy glosowanie
	public void endVote(){
		for(int i = 0; i<terminale.size(); i++){
			Info info = new Info(stanO.getNr(),2);
			try {
				sendDown(terminale.get(i), info);
			} catch (Exception e) {
				e.printStackTrace();
				e.getMessage();
			}
		}		
		stanO.setKonczenieGlosowania(true);
		stanO.setGlosowanieRozpoczete(false);
		//stanO.setZakonczycGlosowanie(false);
		MainWindowO.zakonczonoGlosowanie();
	}
	
	// konczy wszystkie watki i zamyka polaczenia
	public void stop() {
		if(sender!=null)
			sender.stop();
		try {
			if(SSocket != null)
				SSocket.close();
			if(serwerTerm.getSocket()!=null)
				serwerTerm.getSocket().close();
			for(Terminal t: terminale){
				if(t.getOis()!=null)
					t.getOis().close();
				if(t.getOos()!=null)
					t.getOos().close();
				if(t.getSocket()!=null)
					t.getSocket().close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public Terminal getTerminal(String data) {
		for(Terminal t: terminale){
			if(t.getId().equals(data))
				return t;
		}
		return null;
	}
}
